<?php
namespace classlib\database;
use classlib\orm;
class mysql extends parser {
	/**
	 * 构造函数初始化对象实例
	 * @param array $params
	 * @param array $options
	 */
    public function __construct($params, $options) {
        parent::__construct($params, $options);
    }
    
    /**
     * 建立数据库连接 否则抛异常
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::connect()
     */
    public function connect() {
        if ($this->options['persistent']) {
            $this->db = @mysql_pconnect($this->params['host'], $this->params['user'], $this->params['password']);
        } else {
            $this->db = @mysql_connect($this->params['host'], $this->params['user'], $this->params['password']);
        }
        if (!$this->db) {
        	throw new \InvalidArgumentException('Mysql connect error:'.mysql_error());
        }
        mysql_select_db($this->params['dbname']);
        if (isset($this->options['charset'])) {
            $this->execute('set names ' . $this->options['charset']);
        }
    }
    
	/**
	 * 将操作解析成SQL 返回SQL
	 * {@inheritDoc}
	 * @see \classlib\orm\parser\parser::parse_sql()
	 */
    public function parse_sql($args, $isprint=false) {
    	if (!isset($args['table']) || empty($args['table'])) {
    		throw new \InvalidArgumentException('You execute sql table is empty.');
    	}
    	$table = $this->sql_table($args['table']);
        switch ($args['mode']) {
            case 'select':
                $sql = 'SELECT '.$this->sql_fields(isset($args['field'])? $args['field']:'*').' FROM '.$table;
                if (isset($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                if (isset($args['group'])) {
                    $sql .= ' GROUP BY '.$this->sql_group($args['group']);
                }
                if (isset($args['order'])) {
                    $sql .= ' ORDER BY '.$this->sql_order($args['order']);
                }
                break;
            case 'update':
                $sql = 'UPDATE '.$table.' SET '.$this->sql_value($args['value'], 'update');
                if (isset($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                break;
            case 'insert':
            	$sql  = ($args['is_replace'])? 'REPLACE':'INSERT';
                $sql .= ' INTO '.$table.' '.$this->sql_value($args['value'], 'insert');
                if (isset($args['duplicate'])) {
                	$sql .= ' ON DUPLICATE KEY UPDATE '.$this->sql_value($args['duplicate'], 'update');
                }
                break;
            case 'delete':
                $sql = 'DELETE FROM ' . $table;
                if (isset($args['where'])) {
                    $sql .= ' WHERE '.$this->sql_where($args['where']);
                }
                break;
        }
        return $sql;
    }
	
    /**
     * 解析添加的值信息
     * @param mix $value
     * @param string $type
     * @return string
     */
    private function encode_value($value, $type) {
    	$result = '';
		switch ($type) {
			case orm::DT_SQL: 
				$result = $value; 
				break;
			case orm::DT_BINARY: 
				$result = '"'.$value.'"';
				break;
			default: 
				$result = is_string($value)? $this->quote($value):$value;
				break;
		}
		return $result;
    }
    
    /**
     * 信息过滤处理
     * @param string $s
     * @return string
     */
    private function quote($s) {
    	$sql = '';
        if ($this->db === null) {
            $this->connect();
        }
        $s = function_exists('mysql_real_escape_string')?
        	mysql_real_escape_string($s, $this->db):mysql_escape_string($s);
        $sql = '\'' . $s . '\'';
        return $sql;
    }
	
    /**
     * 解析添加的查询资料fields
     * @param array $fields
     * @return string
     */
    private function sql_fields($fields) {
    	if (!is_array($fields)) {
    		return $fields;
    	}
		$sql = array();
        foreach ($fields as $field) {
            $sql[] = $field['alias']? $field['name'].' '.orm::OP_AS.' '.$field['alias']:$field['name'];
        }
        $sql = empty($sql)? '*':implode(',', $sql);
        return $sql;
    }
    
    //解析添加的值资料
    private function sql_value($values, $type) {
    	if (!is_array($values)) {
    		return '';
    	}
        if ($type == 'update') { //更新记录的Value部分
        	$sql  = array();
            foreach ($values as $field=>$value) {
                $sql[] = '`'.$field.'`='.$this->encode_value($value['value'], $value['type']);
            }
            $sql  = implode(',', $sql);
        } else { //插入记录的Value部分
            $svalue = $sfield = array();
            foreach ($values as $field=>$value) {
                $sfield[] = '`'.$field.'`';
                $svalue[] = $this->encode_value($value['value'], $value['type']);
            }
            $sql = '(' . implode(',', $sfield) . ')VALUES(' . implode(',', $svalue) . ')';
        }
        return $sql;
    }
    
    /**
     * 解析操作的表记录
     * @param array $tables
     * @return string|unknown
     */
    private function sql_table($tables) {
        $table = array_shift($tables);
        $sql   = ($table['alias'])? $table['name'].' '.orm::OP_AS.' '.$table['alias']:$table['name'];
        foreach ($tables as $table) {
        	$name  = $table['alias']? $table['name'].' '.orm::OP_AS.' '.$table['alias']:$table['name'];
        	$sql  .= $table['on']? ' LEFT JOIN '.$name.' ON ('.$table['on'].')':','.$name;
        }
        return $sql;
    }
    
    /**
     * 解析操作的Where记录
     * @param array $wheres
     * @return string
     */
    private function sql_where($wheres) {
        $sql 	 = array();
        $logical = false;
        foreach ($wheres as $where) {
            if ($logical && $where['name'] != ')' || $where['logical'] == orm::OP_NOT) {
                $sql[]   = $where['logical'];
            }
            if ($where['name'] == '(' || $where['name'] == ')') {
                $sql[]   = $where['name'];
                $logical = ($where['name'] != '(');
                continue;
            }
			switch ($where['opt']) {
				case orm::OP_SQL:
                	$sql[] = $where['name'];
                    break;
    			case orm::OP_BETWEEN:
    				if (is_array($where['value']) && count($where['value']) == 2) {
                        $sql[] = $where['name'].' '.orm::OP_BETWEEN.' '.implode(' AND ', $where['value']);
					}
					break;
				case orm::OP_IN:
				case orm::OP_NOTIN:
					if (is_array($where['value'])) {
						$sql[] = $where['name'].' '.$where['opt'].'('.implode(',', $where['value']).')';
    				} else {
						$sql[] = $where['name'].' '.$where['opt'].'('.$where['value'].')';
					}
					break;
				case orm::OP_ISNULL:
                case orm::OP_ISNOTNULL:
                	$sql[] = $where['name'].' '.$where['opt'];
                    break;
                default :
                	$sql[] = $where['name'].' '.$where['opt'].' '.$this->encode_value($where['value'], $where['type']);
                    break;
            }
            $logical = true;
        }
        return implode(' ', $sql);
    }
    
    /**
     * 解析操作的组资料
     * @param array $groups
     * @return string
     */
    private function sql_group($groups) {
        $sql = implode(',', $groups);
        return $sql;
    }
    
    /**
     * 解析操作的排序资料
     * @param array $orders
     * @return string
     */
    private function sql_order($orders) {
        $sql = array();
        foreach ($orders as $order) {
            $sql[] = $order['name'] . ' ' . $order['order'];
        }
        $sql = implode(',', $sql);
        return $sql;
    }
    
    /**
     * 获取字段资料信息
     * @param unknown $dataset
     * @param unknown $fetch_mode
     */
    private function fetch($dataset, $fetch_mode=orm::FETCH_ASSOC) {
        switch ($fetch_mode) {
            case orm::FETCH_ASSOC:  $result = mysql_fetch_assoc($dataset);   break;
            case orm::FETCH_NUM:    $result = mysql_fetch_row($dataset);     break;
            case orm::FETCH_BOTH:   $result = mysql_fetch_array($dataset);   break;
            case orm::FETCH_OBJ:    $result = mysql_fetch_object($dataset);  break;
        }
        return $result;
    }
 	
    /**
     * 获取首条记录
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::get_first()
     */
    public function get_first($sql, $fetch_mode = orm::FETCH_ASSOC) {
        $result = $this->execute($sql.' LIMIT 1');
        $data   = $this->fetch($result, $fetch_mode);
        mysql_free_result($result);
        return $data;
    }
    
    /**
     * 获取指定记录或者全部记录
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::get_all()
     * return array
     */
    public function get_all($sql, $offset=0, $count=-1, $fetch_mode=orm::FETCH_ASSOC) {
        $list = array();
        if ($count > 0) {
            $sql .= ' LIMIT ' . $offset . ',' . $count;
        }
        $result = $this->execute($sql);
        while ($row = $this->fetch($result, $fetch_mode)) {
            $list[] = $row;
        }
        mysql_free_result($result);
        return $list;
    }
    
    /**
     * 执行SQL查询
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::execute()
     * @return resultset
     */
    public function execute($sql) {
        if (!$this->db) {
            $this->connect();
        }
        $result = mysql_query($sql, $this->db);
        if ($result === false) {
        	throw new \RuntimeException('Sql execute error:'.mysql_error($this->db), mysql_errno($this->db));
        }
        return $result;
    }
    
    /**
     * 获取insert或者update影响的行
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::get_affected_rows()
     * @return int
     */
    public function get_affected_rows() {
    	return mysql_affected_rows($this->db);
    }
    
    /**
     * 获取Insert的ID
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::last_insert_id()
     * @return int
     */
    public function last_insert_id() {
        return mysql_insert_id($this->db);
    }
    
    /**
     * 关闭数据连接
     * {@inheritDoc}
     * @see \classlib\orm\parser\parser::close()
     */
    public function close() {
    	if (!$this->options['persistent'] && $this->db) {
    		@mysql_close($this->db);
    		$this->db = null;
    	}
    }
}